﻿using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing.Design;
using System.Linq;
using System.Reflection;

namespace N2.ComponentModel.Design
{
	public class AssemblyNameCollectionEditor: CollectionEditor
	{
		#region Types

		class NamedItemWrapper
		{
			public NamedItemWrapper() {}
			
			public NamedItemWrapper(AssemblyName aName)
			{
				this.Assembly = aName;
			}

			[Editor(typeof(AssemblyNameEditor), typeof(UITypeEditor))]
			public AssemblyName Assembly { get; set; }
		}

		#endregion Types

		#region Constructors
		
		public AssemblyNameCollectionEditor(Type type) : base(type) { }

		#endregion Constructors

		#region Methods

		protected override Type[] CreateNewItemTypes()
		{
			return new[] { typeof(NamedItemWrapper) };
		}

		//Wrap items to enable custom type editor
		protected override object[] GetItems(object editValue)
		{
			var _collection = editValue as ICollection;
			return (
				from _aName in _collection.Cast<AssemblyName>()
				select new NamedItemWrapper(_aName)
			).ToArray();
		}

		//Unwrap..
		protected override object SetItems(object editValue, object[] value)
		{
			return base.SetItems(editValue, value.Cast<NamedItemWrapper>().Select(_w => _w.Assembly).ToArray());
		}
		
		//Meaningfull caption for wrapped items
		protected override string GetDisplayText(object value)
		{
			var _wrapper = value as NamedItemWrapper;

			return
				null != _wrapper.Assembly
					? _wrapper.Assembly.Name
					: "(Assembly)";
		}

		//Assemblies should be unique
		protected override bool CanSelectMultipleInstances()
		{
			return false;
		}

		public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
		{
			var _values = base.EditValue(context, provider, value) as IList;
			
			//Discard uninitialised entries
			(	from _empty in _values.Cast<AssemblyName>()
				where null == _empty || string.IsNullOrEmpty(_empty.CodeBase)
				select _empty
			).ToList().ForEach(_values.Remove);
			
			//Remove non-unique entries
			(	from _aName in _values.Cast<AssemblyName>()
				group _aName by _aName.FullName into _group
				where _group.Count() > 1
				from _redundant in _group.Skip(1)
				select _redundant
			).ToList().ForEach(_values.Remove);
			
			return _values;
		}

		#endregion Methods
	}
}
